package Client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;

import Server.Log;

/**
 * Object to handle query packaging and transmission.
 * 
 * @author the_james_marq
 * @version 11 October 13
 */
public class UDPQuery {

	/**
	 * Constant for timeout of ten seconds.
	 */
	private static int TEN_SEC = 10000;
	/**
	 * Arguments passed from main method.
	 */
	private String[] args;
	/**
	 * Hostname of server.
	 */
	private String host;
	/**
	 * Port to initialize socket on.
	 */
	private int port;
	/**
	 * Address of server to send query to.
	 */
	private InetAddress address;
	/**
	 * Socket to transmit on.
	 */
	private DatagramSocket socket;
	/**
	 * Byte buffer for data returned from server.
	 */
	private byte[] ret_buf;
	/**
	 * ASCII character set for String to byte array conversion.
	 */
	private Charset c;

	/**
	 * Constructor for a UDPQuery.
	 * 
	 * @param the_args Arguments passed form main method
	 */
	public UDPQuery(String[] the_args) {
		args=the_args;
		host = args[0];
		port = Integer.parseInt(args[1]);

		try {
			address = InetAddress.getByName(host);
		} catch (UnknownHostException e) {
			Log.err("Unable to resolve host " + host);
		}

		try {
			socket = new DatagramSocket();
		} catch (SocketException e) {
			Log.err("Unable to open socket.");
		}

		ret_buf = new byte[512];
		c = Charset.forName("US-ASCII");
	}

	/**
	 * Sends and receives a UDP query to server.
	 * 
	 * @throws IOException Exception thrown on failure to write to byte stream.
	 */
	public void run() throws IOException{


		for(int i=2; i<args.length; i++) {
			ret_buf = new byte[512]; 
			ByteArrayOutputStream byte_str = new ByteArrayOutputStream();
			byte_str.write(args[i].getBytes(c));
			byte_str.write(0);

			//byte[] data = args[i].getBytes(c);
			byte[] data = byte_str.toByteArray();
			byte_str.close();

			DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
			Log.out("Datagram socket opened on port " + port);

			socket.send(packet);
			socket.setSoTimeout(TEN_SEC);

			packet = new DatagramPacket(ret_buf, ret_buf.length);

			//Watch for timeout while waiting for response.
			try {
				socket.receive(packet);
			} catch (SocketTimeoutException e) {
				Log.err("Connection timeout for query word " + args[i] + ". Proceeding to next query.\n");
				continue;
			}
			data = packet.getData();
			int responses_count = 0;

			String[] ret_strings = new String(data).split("\0");
			if(ret_strings.length>1) {
				responses_count = Integer.parseInt(ret_strings[1].substring(0,1));
				ret_strings[1] = ret_strings[1].substring(1,ret_strings[1].length());
			}


			if(!ret_strings[0].equals(args[i])) {
				Log.err("Unsolicited response for query word " + args[i] +"\n");
				continue;
			}

			//Check the two characters following the sent word for nulls to indicate response type.
			if(data[(args[i].length())*2] == 0 && data[((args[i].length())*2)+1] == 0) {
				Log.out("The word " + args[i] + " was in the dictionary!\n");
				continue;
			} else {
				if(responses_count == 0) {
					Log.out(args[i] + " is spelled incorrectly. There are no suggested words.");
				} else {

					String suggestions = "";
					for(String s : ret_strings) {
						if (s == ret_strings[0]) {continue;}
						//Log.out(s);
						suggestions = suggestions + s + ", ";
					}

					suggestions = suggestions.substring(0, suggestions.length()-2);

					Log.out(args[i] + " is spelled incorrectly. There " + responses_count + " suggested words: " + suggestions + "\n");
				}
			}	
		}

		socket.close();

	}

}
